package com.sanyue.springboot.config;

import com.sanyue.springboot.bean.Cat;
import com.sanyue.springboot.bean.Dog;
import com.sanyue.springboot.bean.Monster;
import com.sanyue.springboot.bean.People;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Scope;


/**
 * 解读：
 *  1.@Configurable 标识这是一个配置类，等价于配置文件
 *  2.程序员可以通过@Bean注解注入bean对象到容器
 *  3.当一个类被@Configurable注解标识，那么该类也会被注入到容器中
 * @author: Guan
 **/
@Configuration
/**
 * 解读：
 *  1.proxyBeanMethods：代理bean的方法
 *  2.Full(proxyBeanMethods = true)：保证每个@bean方法（类中所有bean方法）被调用多少次返回的组件都是单实例的，
 *    是代理方式
 *  3.Lite(proxyBeanMethods = false)【每个@Bean 方法被调用多少次返回的组件都是新创
 *    建的, 是非代理方式】
 *  4.特别说明: proxyBeanMethods 是在 调用@Bean 方法 才生效，因此，需要先获取
 *    BeanConfig 组件，再调用方法。
 *    而不是直接通过 SpringBoot 主程序得到的容器来获取 bean, 注意观察直接通过
 *    ioc.getBean() 获取 Bean, proxyBeanMethods 值并没有生效
 *  5.如何选择: 组件依赖必须使用 Full 模式默认。如果不需要组件依赖使用 Lite 模
 *  6.Lite 模式 也称为轻量级模式，因为不检测依赖关系，运行速度快
 *  补充：如果我们不知道如何选择，使用默认，即不配置。符合springboot的约定——约定优于配置
 */
// @Configuration(proxyBeanMethods = false)
/**
 *
 * 解读：
 *  Class<?>[] value();
 *  1.@Import注解可以指定class类型的数组，注入指定的bean
 *  2.通过@Import注解方式注入的组件，默认组件名字（id）为对应类型的全类名
 *    beanDefinitionName = com.sanyue.springboot.bean.Cat
 *    beanDefinitionName = com.sanyue.springboot.bean.Dog
 */

// @Import({Cat.class, Dog.class})


/**
 *  表示该类所有的组件（bean）都需要进行条件装配
 *  在本文中表示：如果容器中有一个组件名为monster03，则配置类的所有组件均可注入到容器中
 *               果容器中有没一个组件名为monster03，则配置类的所有组件都不会注入到容器中
 */
// @ConditionalOnBean(name = "monster03")

/**
 * 1.启用ConfigurationProperties（配置绑定）功能
 * 2.将指定组件注入到容器中
 * 解读：
 *  1.当@ConfigurationProperties(prefix = "people01")修饰的类没有被@Component修饰，则会报错
 *  2.我们可以再配置类中用@EnableConfigurationProperties({People.class})注解修饰
 *  启用ConfigurationProperties注解
 *  3.了解即可
 */
// @EnableConfigurationProperties({People.class})
public class BeanConfig {


    /**
     * 解读：
     *  1.@Bean：给容器添加组件
     *  2.monster01()：默认，方法名就是bean的id值（名字）
     *  3.Monster：注入类型，即bean的类型就是Monster
     *  4.name属性，通过该属性可以指定bean的名字/id值
     *  5.默认为单例注入
     *  6.
     * @return ：Monster(200,"dr",300,"lu");注入到容器bean的信息
     */
    @Bean(name = "monster03")
    // @Bean
    //将bean更改为多例的，更改value值（值虽然相同，但是不是一个对象，hash值不同）
    // @Scope("prototype")
    public Monster monster01(){
        return new Monster(200,"dr",300,"lu");
    }


    /**
     * 条件装配
     * 解读：
     *  1.@Condition是一个根注解，@ConditionalOnBean是其中一个扩展注解
     *  2.@ConditionalOnBean(name = "monster03")表示
     *    当容器中有一个bean，名字是monster03，我们就将这个bean（dog01）注入到容器中
     *    如果该bean的名字不是monster03，就不注入
     *  3.还有很多其他的条件约束注解，自行测试和了解
     *  注意：是当容器中有一个bean，不是这个我们设置的这个bean
     *  4.@ConditionalOnMissingBean(name = "monster03")
     *    如果容器中有一个bean，名字是monster03，则dog01不注入到容器中
     *    即在容器中，没有名字或id为monster03，则该bean注入到容器中
     *  5.@Target({ElementType.TYPE, ElementType.METHOD})
     *    @ConditionalOnMissingBean(name = "monster03")也可以配置类
     *    详情看类上的注解
     *
     */
    @Bean
    @ConditionalOnBean(name = "monster03")
    // @ConditionalOnMissingBean(name = "monster03")
    public Dog dog01(){
        return new Dog();
    }


}
